/*
 * Copyright (c) 2023 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include "smc.h"
#include <mc/exception.h>
#include <mc/crypto/crc8.h>

using namespace dev;

namespace {
const uint32_t SMC_MAX_DATA_LENGTH = 255;

const uint32_t SMC_MAX_BUFFER_LEN = SMC_MAX_DATA_LENGTH + 5;
const uint32_t SMC_MIN_READ_LEN = 2;
const uint32_t SMC_FORWARD_READ_LEN = 3;
const uint32_t SMC_DEFAULT_READ_LEN = 8;
const uint32_t SMC_MAX_BYTE_READ_SIZE = 4;
const uint32_t BITS_PER_BYTE = 8;
const uint32_t SMC_EXTRA_LEN = 1;
const uint32_t SMC_READ_LEN_OFFSET = 3;
const uint32_t SMC_READ_CC_OFFSET = SMC_READ_LEN_OFFSET + 1;
const uint32_t SMC_READ_DATA_OFFSET = SMC_READ_CC_OFFSET + 1;
const uint32_t SMC_RESP_RAW_READ_LEN_OFFSET = 0x00;
const uint32_t SMC_RESP_RAW_CC_OFFSET = 0x01;

const uint32_t SMC_LISTEN_TIMES = 4;
const uint32_t SMC_SLEEP_TIME = 5000;

const uint32_t SMC_MULTI_MASK = 0x200;
const uint32_t SMC_RW_MASK = 0x100;
const uint32_t SMC_OPCODE_MASK = 0xfffffc00;
const uint32_t SMC_PARA_MASK = 0xff;

inline static uint8_t get_first_nonzero_pos(uint32_t mask)
{
    return __builtin_ctz(mask);
}

template <typename T> inline static T set_value_with_mask(uint32_t mask, T oriData, T setData)
{
    uint8_t shift = get_first_nonzero_pos(mask);
    return static_cast<T>((mask & (setData << shift)) | (oriData & (~mask)));
}

template <typename T> inline static T get_value_with_mask(uint32_t mask, T oriData)
{
    std::cout << "get_value_with_mask mask: " << std::hex << mask << " oriData: " << std::hex << oriData << std::endl;
    uint8_t shift = get_first_nonzero_pos(mask);
    std::cout << "shift: " << std::dec << static_cast<uint32_t>(shift) << std::endl;
    return static_cast<T>((oriData & mask) >> shift);
}
}; // namespace

bool chip_smc::init(mc::mutable_dict& csr_object) {
    try {
        // 调用chip_base的init函数
        chip_base::init(csr_object);
    } catch (const std::exception& e) {
        elog("SMC configuration loading failed: ${error}", ("error", e.what()));
        return false;
    }

    return true;
}

int32_t chip_smc::bit_read(uint32_t offset, uint8_t size, uint32_t mask,
    std::vector<uint8_t>& in_buffer, std::vector<uint8_t>& out_buffer,
    std::string& error) {
    if (size == 8 || size > SMC_MAX_BYTE_READ_SIZE) {
        error = "bit_read invalid size";
        return HAS_ERR;
    }

    if (mask == 0) {
        out_buffer.clear();
        return HAS_OK;
    }

    out_buffer.clear();

    uint8_t read_buffer[SMC_MAX_BUFFER_LEN] = {0};
    uint32_t ret = normal_read((offset & SMC_OPCODE_MASK) + 0x100, SMC_MAX_BUFFER_LEN, read_buffer, error);
    if (ret != HAS_OK) {
        return ret;
    }

    uint32_t para = offset & SMC_PARA_MASK;
    if (para > 0) {
        para--;
    }

    uint32_t data_length = read_buffer[SMC_READ_LEN_OFFSET] - SMC_EXTRA_LEN;
    if (para * size + size > data_length) {
        error = "bit_read para size overflow";
        return HAS_ERR;
    }

    uint8_t *dataBuf = read_buffer + SMC_READ_DATA_OFFSET + para * size;

    switch (size) {
        case sizeof(uint8_t):
            out_buffer.push_back(get_value_with_mask(mask, dataBuf[0]));
            break;
        case sizeof(uint16_t): {
            uint16_t value = get_value_with_mask(mask, *((uint16_t*)dataBuf));
            out_buffer.push_back(value & 0xFF);
            out_buffer.push_back(value >> 8);
            break;
        }
        case sizeof(uint32_t): {
            uint32_t value = get_value_with_mask(mask, *((uint32_t*)dataBuf));
            out_buffer.push_back(value & 0xFF);
            out_buffer.push_back((value >> 8) & 0xFF);
            out_buffer.push_back((value >> 16) & 0xFF);
            out_buffer.push_back(value >> 24);
            break;
        }
        default:
            error = "bit_read invalid size";
            return HAS_ERR;
    }

    return HAS_OK;
}

int32_t chip_smc::bit_write(uint32_t offset, uint8_t size, uint32_t mask,
    const std::vector<uint8_t>& in_buffer,
    std::vector<uint8_t>&       out_buffer,
    std::string& error) {
    if (size > SMC_MAX_BYTE_READ_SIZE || mask == 0) {
        error = "bit_write invalid size or mask";
        return HAS_ERR;
    }

    uint32_t para = offset & SMC_PARA_MASK;
    if (para == 0 && ((1U << (BITS_PER_BYTE * size)) - 1) == mask) {
        // mask 全为1时不需要读取硬件值, 直接写入硬件
        return write(offset, size, in_buffer.data(), error);
    }

    if (para > 0) {
        para--;
    }

    uint8_t read_buffer[SMC_MAX_BUFFER_LEN] = {0};
    uint32_t ret = normal_read((offset & SMC_OPCODE_MASK) + 0x100, SMC_MAX_BUFFER_LEN, read_buffer, error);
    if (ret != HAS_OK) {
        return ret;
    }

    uint32_t data_length = read_buffer[SMC_READ_LEN_OFFSET] - 1;
    if (para * size + size > data_length) {
        error = "bit_write para size overflow";
        return HAS_ERR;
    }

    uint8_t *dataBuf = read_buffer + SMC_READ_DATA_OFFSET + para * size;
    switch (size) {
        case sizeof(uint8_t):
            *((uint8_t*)dataBuf) = set_value_with_mask(mask, *((uint8_t*)dataBuf), in_buffer[0]);
            break;
        case sizeof(uint16_t):
            *((uint16_t*)dataBuf) = set_value_with_mask(mask, *((uint16_t*)dataBuf), *((uint16_t*)in_buffer.data()));
            break;
        case sizeof(uint32_t):
            *((uint32_t*)dataBuf) = set_value_with_mask(mask, *((uint32_t*)dataBuf), *((uint32_t*)in_buffer.data()));
            break;
        default:
            error = "bit_write invalid size";
            return HAS_ERR;
    }

    return write(offset, size, dataBuf, error);
}

int32_t chip_smc::block_read(uint32_t offset, uint32_t length, std::vector<uint8_t>& in_buffer,
    std::vector<uint8_t>& out_buffer, std::string& error) {
    out_buffer.clear();

    auto ret = read(offset, length, out_buffer, error);
    out_buffer.shrink_to_fit();

    return ret;
}

int32_t chip_smc::block_write(uint32_t offset, const std::vector<uint8_t>& in_buffer,
    std::vector<uint8_t>& out_buffer, std::string& error) {
    return write(offset, in_buffer.size(), in_buffer.data(), error);
}

int32_t chip_smc::block_write_read(uint32_t offset, const std::vector<uint8_t>& indata,
    uint32_t read_length, std::vector<uint8_t>& out_buffer,
    std::string& error) {
    MC_THROW(mc::invalid_op_exception, "Chip SMC block_write_read not supported");
}

uint32_t chip_smc::read_data(uint32_t read_length, uint8_t* out_buffer, uint8_t command_code, std::string& error) {
    std::vector<uint8_t> in_buffer_vec;
    std::vector<uint8_t> out_buffer_vec;
    out_buffer_vec.reserve(read_length + SMC_READ_LEN_OFFSET);
    for (uint32_t i = 0; i < SMC_LISTEN_TIMES; i++) {
        usleep(pow(2, i) * 1000);

        try {
            uint32_t ret = data_access(0, command_code, read_length, in_buffer_vec, out_buffer_vec, CHIP_OP_TYPE_READ);
            if (ret != HAS_OK) {
                error = "data_access failed";
                elog("data_access failed, ret: ${ret}", ("ret", ret));
                return ret;
            }
        } catch (const std::exception& e) {
            error = std::string("read_data error: ") + e.what();
            elog("${error}", ("error", error));
            return HAS_ERR;
        }

        if (out_buffer_vec.at(SMC_RESP_RAW_CC_OFFSET) != SMC_CC_SUCCESS) {
            if (out_buffer_vec.at(SMC_RESP_RAW_CC_OFFSET) == SMC_CC_NOT_READY) {
                continue;
            } else {
                error = "read_data failed, unexpected status: " + std::to_string(out_buffer_vec.at(SMC_RESP_RAW_CC_OFFSET));
                elog("${error}", ("error", error));
                return HAS_ERR;
            }
        }

        if (static_cast<uint32_t>(out_buffer_vec.at(SMC_RESP_RAW_READ_LEN_OFFSET) + SMC_MIN_READ_LEN) > read_length) {
            read_length = out_buffer_vec.at(SMC_RESP_RAW_READ_LEN_OFFSET) + SMC_MIN_READ_LEN;
            continue;
        }

        out_buffer_vec.resize(out_buffer_vec.at(SMC_RESP_RAW_READ_LEN_OFFSET) + SMC_MIN_READ_LEN);

        // 倒序插入, 拼凑原始响应数据计算响应crc
        out_buffer_vec.insert(out_buffer_vec.begin(), m_address | 1);
        out_buffer_vec.insert(out_buffer_vec.begin(), command_code);
        out_buffer_vec.insert(out_buffer_vec.begin(), m_address & ~1);

        // 构建crc_input时去掉最后一个字节的响应crc
        std::string crc_input(out_buffer_vec.begin(), out_buffer_vec.end() - 1);    
        uint8_t real_length = out_buffer_vec.at(SMC_READ_LEN_OFFSET);
        uint8_t crc8 = mc::crypto::CRC8().calculate(crc_input);
        if (crc8 != out_buffer_vec.back()) {
            error = "crc mismatch, crc8: " + std::to_string(crc8) + ", expected: " + std::to_string(out_buffer_vec.back()); 
            elog("${error}", ("error", error));
            return HAS_ERR;
        }

        // 将响应数据复制到输出缓冲区
        memcpy(out_buffer, out_buffer_vec.data(), out_buffer_vec.size());

        return HAS_OK;
    }

    error = "read_data failed, max listen times reached";
    elog("${error}", ("error", error));
    return HAS_ERR;
}

void chip_smc::get_length_from_map(uint32_t offset, uint32_t& record_length) {
    auto it = m_opcode_length_map.find(offset);
    if (it != m_opcode_length_map.end()) {
        record_length = it->second;
    } else {
        record_length = SMC_DEFAULT_READ_LEN;
    }
}

void chip_smc::update_length_to_map(uint32_t offset, uint32_t length) {
    m_opcode_length_map[offset & SMC_OPCODE_MASK] = length;
}

uint32_t chip_smc::read_from_hardware(uint32_t opcode, uint32_t out_length, uint8_t* out_buffer, std::string& error) {
    uint32_t ret = write(opcode, 0, NULL, error);
    if (ret != HAS_OK) {
        return ret;
    }

    uint32_t real_length = (out_length < SMC_MAX_DATA_LENGTH) ? out_length + SMC_MIN_READ_LEN : SMC_MIN_READ_LEN;

    return read_data(real_length, out_buffer, SMC_READ_BUFFER, error);
}

uint32_t chip_smc::normal_read(uint32_t offset, uint32_t buffer_length, uint8_t* buffer, std::string& error) {
    uint32_t record_length = 0;
    get_length_from_map(offset, record_length);

    if (read_from_hardware(offset, record_length, buffer, error) != HAS_OK) {
        return HAS_ERR;
    }

    update_length_to_map(offset, buffer[SMC_READ_LEN_OFFSET]);
    return HAS_OK;
}

uint32_t chip_smc::normal_write(uint32_t offset, uint32_t length, const uint8_t* buffer, uint8_t command_code, std::string& error) {
    if (length > SMC_MAX_DATA_LENGTH - sizeof(offset)) {
        error = "length is too long";
        return HAS_ERR;
    }

    std::string crc_input;
    crc_input += static_cast<char>(m_address);
    crc_input += static_cast<char>(command_code);
    crc_input += static_cast<char>(sizeof(length) + length);
    for (uint32_t i = sizeof(offset); i > 0; i--) {
        crc_input += static_cast<char>((offset >> ((i - 1) * 8)) & 0xFF);
    }
    if (length != 0) {
        crc_input += std::string(reinterpret_cast<const char*>(buffer), length);
    }

    uint8_t crc8 = mc::crypto::CRC8().calculate(crc_input);
    crc_input += static_cast<char>(crc8);

    // 跳过前两个字节，因为前两个字节是地址和命令码, 命令码作为offset直接使用
    std::vector<uint8_t> in_buffer_vec(crc_input.begin() + 2, crc_input.end());
    std::vector<uint8_t> out_buffer_vec;

    try {
        uint32_t ret = data_access(0, crc_input[1], in_buffer_vec.size(), in_buffer_vec, out_buffer_vec, CHIP_OP_TYPE_WRITE);
        if (ret != HAS_OK) {
            error = "data_access failed";
        }
        return ret;
    } catch (const std::exception& e) {
        error = std::string("normal_write error: ") + e.what();
        elog("${error}", ("error", error));
        return HAS_ERR;
    }
}

uint32_t chip_smc::read(uint32_t offset, uint32_t length, std::vector<uint8_t>& buffer, std::string& error) {
    if (length == 0) {
        return HAS_ERR;
    }

    uint8_t read_buffer[SMC_MAX_BUFFER_LEN] = {0};
    uint32_t ret = normal_read(offset, SMC_MAX_BUFFER_LEN, read_buffer, error);
    if (ret != HAS_OK) {
        elog("normal_read failed, ret: ${ret}", ("ret", ret));
        return ret;
    }

    for (uint32_t i = 0; i < length && i < read_buffer[SMC_READ_LEN_OFFSET] - 1; i++) {
        buffer.push_back(read_buffer[i + SMC_READ_DATA_OFFSET]);
    }
    return HAS_OK;
}

uint32_t chip_smc::write(uint32_t offset, uint32_t length, const uint8_t* buffer, std::string& error) {
    uint8_t command_code = (length == 0) ? SMC_WRITE_OPCODE : SMC_WRITE_OPCODE_WITH_DATA;

    return normal_write(offset, length, buffer, command_code, error);
}

uint32_t chip_smc::forward_read(uint32_t length, uint8_t* data_out) {
    uint8_t out_buffer[SMC_MAX_BUFFER_LEN] = {0};

    std::string error;
    uint32_t read_length = (length < SMC_MAX_DATA_LENGTH) ? length + SMC_FORWARD_READ_LEN : SMC_FORWARD_READ_LEN;
    uint32_t ret = read_data(read_length, out_buffer, SMC_READ_BUFFER_FORWARD, error);
    if (ret != HAS_OK) {
        return ret;
    }

   for (uint32_t i = 0; i < length && i < out_buffer[SMC_READ_LEN_OFFSET] - 1U; i++) {
        data_out[i] = out_buffer[i + SMC_READ_DATA_OFFSET];
    }

    return ret;
}

uint32_t chip_smc::forward_write(uint32_t offset, uint32_t length, uint8_t* buffer) {
    std::string error;
    return normal_write(offset, length, buffer, SMC_WRITE_OPCODE_FORWARD, error);
}

uint32_t chip_smc::calc_forward_opcode(uint8_t addr, uint32_t length, uint8_t chip_type, uint8_t bus_id) {
    constexpr uint32_t ADDR_LEFT_SHIFT      = 24;
    constexpr uint32_t LEN_LEFT_SHIFT       = 16;
    constexpr uint32_t CHIP_TYPE_LEFT_SHIFT = 8;
    constexpr uint32_t BUS_ID_LEFT_SHIFT    = 0;
    uint32_t offset = 0;

    offset |= (addr << ADDR_LEFT_SHIFT);
    offset |= (length << LEN_LEFT_SHIFT);
    offset |= (chip_type << CHIP_TYPE_LEFT_SHIFT);
    offset |= (bus_id << BUS_ID_LEFT_SHIFT);

    return offset;
}

int32_t chip_smc::forward_by_front_chip(ACCESS_DATA_S& data, const std::string& class_name, uint8_t bus_id) {
    constexpr uint32_t BUFFER_LEN = 250;
    bool is_smc = class_name == "smc";
    int result = HAS_OK;
    if (data.optype == CHIP_OP_TYPE_WRITE) {
        uint8_t buffer[BUFFER_LEN] = {0};
        uint8_t chip_type = 0x00;
        if (is_smc) {
            chip_type = 0x04;
        }

        uint32_t offset = calc_forward_opcode(data.addr, data.length, chip_type, bus_id);
        uint32_t buffer_offset = 0;
        for (uint32_t i = data.offset_width; i > 0; i--) {
            buffer[buffer_offset++] = (data.offset >> ((i - 1) * 8)) & 0xFF;
        }

        memcpy(buffer + buffer_offset, data.in_buffer.data(), data.length);

        return forward_write(offset, data.length + buffer_offset, buffer);
    }

    // 如果是eeprom, 要补上写操作, 主要就是需要写offset
    if (!is_smc) {
        uint8_t buffer[BUFFER_LEN] = {0};
        uint8_t chip_type = 0x00;

        uint32_t offset = calc_forward_opcode(data.addr | 0x01, data.length, chip_type, bus_id);
        uint32_t buffer_offset = 0;
        for (uint32_t i = data.offset_width; i > 0; i--) {
            buffer[buffer_offset++] = (data.offset >> ((i - 1) * 8)) & 0xFF;
        }

        result = forward_write(offset, buffer_offset, buffer);
        if (result != HAS_OK) {
            return result;
        }
        usleep(SMC_SLEEP_TIME);
    }

    return forward_read(data.length, data.out_buffer.data());
}

const char* chip_smc::get_class_name() const {
    return "smc";
}
